/**
 ****************************************************************************************************
 * @file        ov7670.c
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2022-06-21
 * @brief       OV7670模块驱动代码
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 探索者 F407开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */

#include "delay.h"
#include "ov7670_cfg.h"
#include "ov7670_dcmi.h"
#include "ov7670_sccb.h"

#if (OV7670_USING_DCMI == 0)
    #include "./MALLOC/malloc.h"
#endif

#if (OV7670_USING_DCMI != 0)
    #include "ov7670.h"
#endif

/* OV7670模块制造商ID和产品ID */
#define OV7670_MID 0x7FA2
#define OV7670_PID 0x7673

/* OV7670模块数据结构体 */
static struct
{
    struct {
        uint16_t width;
        uint16_t height;
    } output;

#if (OV7670_USING_DCMI == 0)
    struct {
        uint8_t* line_buf;
        DMA_HandleTypeDef dma_handle;
    } read;
#endif
} g_ov7670_sta = {0};

/**
 * @brief       OV7670模块写寄存器
 * @param       reg: 寄存器地址
 *              dat: 待写入的值
 * @retval      无
 */
static void ov7670_write_reg(uint8_t reg, uint8_t dat) {
    ov7670_sccb_3_phase_write(OV7670_SCCB_ADDR, reg, dat);
}

/**
 * @brief       OV7670模块读寄存器
 * @param       reg: 寄存器的地址
 * @retval      读取到的寄存器值
 */
static uint8_t ov7670_read_reg(uint8_t reg) {
    uint8_t dat = 0;

    ov7670_sccb_2_phase_write(OV7670_SCCB_ADDR, reg);
    ov7670_sccb_2_phase_read(OV7670_SCCB_ADDR, &dat);

    return dat;
}

/**
 * @brief       OV7670模块硬件初始化
 * @param       无
 * @retval      无
 */
static void ov7670_hw_init(void) {
    GPIO_InitTypeDef gpio_init_struct = {0};

    /* 使能GPIO时钟 */
#if (OV7670_USING_DCMI == 0)
    OV7670_VSYNC_GPIO_CLK_ENABLE();
    OV7670_HREF_GPIO_CLK_ENABLE();
    OV7670_D0_GPIO_CLK_ENABLE();
    OV7670_D1_GPIO_CLK_ENABLE();
    OV7670_D2_GPIO_CLK_ENABLE();
    OV7670_D3_GPIO_CLK_ENABLE();
    OV7670_D4_GPIO_CLK_ENABLE();
    OV7670_D5_GPIO_CLK_ENABLE();
    OV7670_D6_GPIO_CLK_ENABLE();
    OV7670_D7_GPIO_CLK_ENABLE();
    OV7670_PCLK_GPIO_CLK_ENABLE();
#endif
    OV7670_RST_GPIO_CLK_ENABLE();
    OV7670_SGM_GPIO_CLK_ENABLE();

#if (OV7670_USING_DCMI == 0)
    /* 初始化VSYNC引脚 */
    gpio_init_struct.Pin = OV7670_VSYNC_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_VSYNC_GPIO_PORT, &gpio_init_struct);

    /* 初始化HREF引脚 */
    gpio_init_struct.Pin = OV7670_HREF_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_HREF_GPIO_PORT, &gpio_init_struct);

    /* 初始化D0引脚 */
    gpio_init_struct.Pin = OV7670_D0_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D0_GPIO_PORT, &gpio_init_struct);

    /* 初始化D1引脚 */
    gpio_init_struct.Pin = OV7670_D1_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D1_GPIO_PORT, &gpio_init_struct);

    /* 初始化D2引脚 */
    gpio_init_struct.Pin = OV7670_D2_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D2_GPIO_PORT, &gpio_init_struct);

    /* 初始化D3引脚 */
    gpio_init_struct.Pin = OV7670_D3_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D3_GPIO_PORT, &gpio_init_struct);

    /* 初始化D4引脚 */
    gpio_init_struct.Pin = OV7670_D4_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D4_GPIO_PORT, &gpio_init_struct);

    /* 初始化D5引脚 */
    gpio_init_struct.Pin = OV7670_D5_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D5_GPIO_PORT, &gpio_init_struct);

    /* 初始化D6引脚 */
    gpio_init_struct.Pin = OV7670_D6_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D6_GPIO_PORT, &gpio_init_struct);

    /* 初始化D7引脚 */
    gpio_init_struct.Pin = OV7670_D7_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_D7_GPIO_PORT, &gpio_init_struct);

    /* 初始化PCLK引脚 */
    gpio_init_struct.Pin = OV7670_PCLK_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_INPUT;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_PCLK_GPIO_PORT, &gpio_init_struct);
#endif

    /* 初始化RST引脚 */
    gpio_init_struct.Pin = OV7670_RST_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_RST_GPIO_PORT, &gpio_init_struct);

    /* 初始化SGM引脚 */
    gpio_init_struct.Pin = OV7670_SGM_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(OV7670_SGM_GPIO_PORT, &gpio_init_struct);

    OV7670_RST(1);
    OV7670_SGM(0);
}

/**
 * @brief       OV7670模块硬件复位
 * @param       无
 * @retval      无
 */
static void ov7670_hw_reset(void) {
    OV7670_RST(0);
    delay_ms(10);
    OV7670_RST(1);
    delay_ms(10);
}

/**
 * @brief       OV7670模块软件复位
 * @param       无
 * @retval      无
 */
static void ov7670_sw_reset(void) {
    ov7670_write_reg(0x12, 0x80);
    delay_ms(50);
}

/**
 * @brief       获取OV7670模块制造商ID
 * @param       无
 * @retval      制造商ID
 */
static uint16_t ov7670_get_mid(void) {
    uint16_t mid;

    mid = ov7670_read_reg(0x1C) << 8;
    mid |= ov7670_read_reg(0x1D);

    return mid;
}

/**
 * @brief       获取OV7670模块产品ID
 * @param       无
 * @retval      产品ID
 */
static uint16_t ov7670_get_pid(void) {
    uint16_t pid;

    pid = ov7670_read_reg(0x0A) << 8;
    pid |= ov7670_read_reg(0x0B);

    return pid;
}

/**
 * @brief       初始化OV7670寄存器配置
 * @param       无
 * @retval      无
 */
static void ov7670_init_reg(void) {
    uint32_t cfg_index;

    for (cfg_index = 0; cfg_index < (sizeof(ov7670_init_cfg) / sizeof(ov7670_init_cfg[0])); cfg_index++) {
        ov7670_write_reg(ov7670_init_cfg[cfg_index][0], ov7670_init_cfg[cfg_index][1]);
        // switch (ov7670_init_cfg[cfg_index][0])
        // {
        //     case OV7670_REG_HSIZE:
        //     {
        //         g_ov7670_sta.output.width = ov7670_init_cfg[cfg_index][1] << 2;
        //         break;
        //     }
        //     case OV7670_REG_VSIZE:
        //     {
        //         g_ov7670_sta.output.height = ov7670_init_cfg[cfg_index][1] << 1;
        //         break;
        //     }
        //     default:
        //     {
        //         break;
        //     }
        // }
    }
    g_ov7670_sta.output.width = OV7670_WIDTH;
    g_ov7670_sta.output.height = OV7670_HEIGHT;
}

#if (OV7670_USING_DCMI == 0)
/**
 * @brief       获取ATK-MD7725端口D0~D7的一字节数据
 * @param       无
 * @retval      ATK-MD7725端口D0~D7的一字节数据
 */
static inline uint8_t ov7670_get_byte_data(void) {
    #if (OV7670_DATA_PIN_IN_SAME_GPIO_PORT == 0)
    uint8_t dat = 0;

    dat |= (((OV7670_D0_GPIO_PORT->IDR & OV7670_D0_GPIO_PIN) == 0) ? (0) : (1)) << 0;
    dat |= (((OV7670_D1_GPIO_PORT->IDR & OV7670_D1_GPIO_PIN) == 0) ? (0) : (1)) << 1;
    dat |= (((OV7670_D2_GPIO_PORT->IDR & OV7670_D2_GPIO_PIN) == 0) ? (0) : (1)) << 2;
    dat |= (((OV7670_D3_GPIO_PORT->IDR & OV7670_D3_GPIO_PIN) == 0) ? (0) : (1)) << 3;
    dat |= (((OV7670_D4_GPIO_PORT->IDR & OV7670_D4_GPIO_PIN) == 0) ? (0) : (1)) << 4;
    dat |= (((OV7670_D5_GPIO_PORT->IDR & OV7670_D5_GPIO_PIN) == 0) ? (0) : (1)) << 5;
    dat |= (((OV7670_D6_GPIO_PORT->IDR & OV7670_D6_GPIO_PIN) == 0) ? (0) : (1)) << 6;
    dat |= (((OV7670_D7_GPIO_PORT->IDR & OV7670_D7_GPIO_PIN) == 0) ? (0) : (1)) << 7;

    return dat;
    #else
    return OV7670_DATE_GPIO_PORT->IDR & OV7670_DATA_READ_MASK;
    #endif
}
#endif

#if (OV7670_USING_DCMI == 0)
/**
 * @brief       初始化OV7670模块DMA
 * @param       meminc          : DMA_MINC_DISABLE: 帧数据接收的目的地址自动增加
 *                                DMA_MINC_ENABLE : 帧数据接收的目的地址不自动增加
 *              memdataalignment: DMA_MDATAALIGN_BYTE    : 帧数据接收缓冲的位宽为8比特
 *                                DMA_MDATAALIGN_HALFWORD: 帧数据接收缓冲的位宽为16比特
 *                                DMA_MDATAALIGN_WORD    : 帧数据接收缓冲的位宽为32比特
 * @retval      无
 */
static void ov7670_dma_init(uint32_t meminc, uint32_t memdataalignment) {
    OV7670_DMA_CLK_ENABLE();

    g_ov7670_sta.read.dma_handle.Instance = OV7670_DMA_INTERFACE;
    g_ov7670_sta.read.dma_handle.Init.Channel = OV7670_DMA_CHANNEL;
    g_ov7670_sta.read.dma_handle.Init.Direction = DMA_MEMORY_TO_MEMORY;
    g_ov7670_sta.read.dma_handle.Init.PeriphInc = DMA_PINC_ENABLE;
    g_ov7670_sta.read.dma_handle.Init.MemInc = meminc;
    g_ov7670_sta.read.dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    g_ov7670_sta.read.dma_handle.Init.MemDataAlignment = memdataalignment;
    g_ov7670_sta.read.dma_handle.Init.Mode = DMA_NORMAL;
    g_ov7670_sta.read.dma_handle.Init.Priority = DMA_PRIORITY_VERY_HIGH;

    HAL_DMA_Init(&g_ov7670_sta.read.dma_handle);
}
#endif

/**
 * @brief       初始化OV7670模块
 * @param       无
 * @retval      OV7670_EOK   : OV7670模块初始化成功
 *              OV7670_ERROR : 通讯出错，OV7670模块初始化失败
 */
uint8_t OV7670_Init(void) {
    uint16_t mid;
    uint16_t pid;

    ov7670_hw_init();   /* OV7670模块硬件初始化 */
    ov7670_hw_reset();  /* OV7670模块硬件复位 */
    ov7670_sccb_init(); /* OV7670 SCCB接口初始化 */
    ov7670_sw_reset();  /* OV7670模块软件复位 */
    ov7670_set_contrast(OV7670_CONTRAST_0);
    mid = ov7670_get_mid(); /* 获取制造商ID */
    if (mid != OV7670_MID) {
        return OV7670_ERROR;
    }

    pid = ov7670_get_pid(); /* 获取产品ID */
    if (pid != OV7670_PID) {
        return OV7670_ERROR;
    }

    ov7670_init_reg(); /* 初始化OV7670寄存器配置 */

#if (OV7670_USING_DCMI == 0)
    g_ov7670_sta.read.line_buf = mymalloc(SRAMIN, g_ov7670_sta.output.width * sizeof(uint16_t));

    if (g_ov7670_sta.read.line_buf == NULL) {
        return OV7670_ENOMEM;
    }
#endif

#if (OV7670_USING_DCMI != 0)
    ov7670_dcmi_init(); /* 初始化OV7670模块DCMI接口 */
#endif

    return OV7670_EOK;
}

/**
 * @brief       设置OV7670模块灯光模式
 * @param       mode: OV7670_LIGHT_MOED_AUTO  : Auto
 *                    OV7670_LIGHT_MOED_SUNNY : Sunny
 *                    OV7670_LIGHT_MOED_CLOUDY: Cloudy
 *                    OV7670_LIGHT_MOED_OFFICE: Office
 *                    OV7670_LIGHT_MOED_HOME  : Home
 *                    OV7670_LIGHT_MOED_NIGHT : Night
 * @retval      OV7670_EOK   : 设置OV7670模块灯光模式成功
 *              OV7670_EINVAL: 传入参数错误
 */
uint8_t ov7670_set_light_mode(ov7670_light_mode_t mode) {
    switch (mode) {
    case OV7670_LIGHT_MODE_AUTO: {
        ov7670_write_reg(0x13, 0xE7);
        ov7670_write_reg(0x01, 0x00);
        ov7670_write_reg(0x02, 0x00);
        break;
    }
    case OV7670_LIGHT_MODE_SUNNY: {
        ov7670_write_reg(0x13, 0xE5);
        ov7670_write_reg(0x01, 0x5A);
        ov7670_write_reg(0x02, 0x5C);
        break;
    }
    case OV7670_LIGHT_MODE_CLOUDY: {
        ov7670_write_reg(0x13, 0xE5);
        ov7670_write_reg(0x01, 0x58);
        ov7670_write_reg(0x02, 0x60);
        break;
    }
    case OV7670_LIGHT_MODE_OFFICE: {
        ov7670_write_reg(0x13, 0xE5);
        ov7670_write_reg(0x01, 0x84);
        ov7670_write_reg(0x02, 0x4c);
        break;
    }
    case OV7670_LIGHT_MODE_HOME: {
        ov7670_write_reg(0x13, 0xE5);
        ov7670_write_reg(0x01, 0x96);
        ov7670_write_reg(0x02, 0x40);
        break;
    }
    default: {
        return OV7670_EINVAL;
    }
    }

    return OV7670_EOK;
}

/**
 * @brief       设置OV7670模块色彩饱和度
 * @param       saturation: OV7670_COLOR_SATURATION_0: +4
 *                          OV7670_COLOR_SATURATION_1: +3
 *                          OV7670_COLOR_SATURATION_2: +2
 *                          OV7670_COLOR_SATURATION_3: +1
 *                          OV7670_COLOR_SATURATION_4: 0
 *                          OV7670_COLOR_SATURATION_5: -1
 *                          OV7670_COLOR_SATURATION_6: -2
 *                          OV7670_COLOR_SATURATION_7: -3
 *                          OV7670_COLOR_SATURATION_8: -4
 * @retval      OV7670_EOK   : 设置OV7670模块色彩饱和度成功
 *              OV7670_EINVAL: 传入参数错误
 */
uint8_t ov7670_set_color_saturation(uint8_t saturation) {
    uint8_t reg4f5054val = 0X80; // 默认就是sat=2,即不调节色度的设置
    uint8_t reg52val = 0X22;
    uint8_t reg53val = 0X5E;
    switch (saturation) {
    case 0: //-2
        reg4f5054val = 0X40;
        reg52val = 0X11;
        reg53val = 0X2F;
        break;
    case 1: //-1
        reg4f5054val = 0X66;
        reg52val = 0X1B;
        reg53val = 0X4B;
        break;
    case 3: // 1
        reg4f5054val = 0X99;
        reg52val = 0X28;
        reg53val = 0X71;
        break;
    case 4: // 2
        reg4f5054val = 0XC0;
        reg52val = 0X33;
        reg53val = 0X8D;
        break;
    }
    ov7670_write_reg(0X4F, reg4f5054val); // 色彩矩阵系数1
    ov7670_write_reg(0X50, reg4f5054val); // 色彩矩阵系数2
    ov7670_write_reg(0X51, 0X00);         // 色彩矩阵系数3
    ov7670_write_reg(0X52, reg52val);     // 色彩矩阵系数4
    ov7670_write_reg(0X53, reg53val);     // 色彩矩阵系数5
    ov7670_write_reg(0X54, reg4f5054val); // 色彩矩阵系数6
    ov7670_write_reg(0X58, 0X9E);         // MTXS

    return OV7670_EOK;
}

/**
 * @brief       设置OV7670模块亮度
 * @param       brightness: OV7670_BRIGHTNESS_0: -2
 *                          OV7670_BRIGHTNESS_1: -1
 *                          OV7670_BRIGHTNESS_2: 0
 *                          OV7670_BRIGHTNESS_3: +1
 *                          OV7670_BRIGHTNESS_4: +2
 * @retval      OV7670_EOK   : 设置OV7670模块亮度成功
 *              OV7670_EINVAL: 传入参数错误
 */
uint8_t ov7670_set_brightness(ov7670_brightness_t brightness) {
    switch (brightness) {
    case OV7670_BRIGHTNESS_0: {
        ov7670_write_reg(0x55, 0XB0);
        break;
    }
    case OV7670_BRIGHTNESS_1: {
        ov7670_write_reg(0x55, 0x98);
        break;
    }
    case OV7670_BRIGHTNESS_2: {
        ov7670_write_reg(0x55, 0X00);
        break;
    }
    case OV7670_BRIGHTNESS_3: {
        ov7670_write_reg(0x55, 0X18);
        break;
    }
    case OV7670_BRIGHTNESS_4: {
        ov7670_write_reg(0x55, 0X30);
        break;
    }
    default: {
        return OV7670_EINVAL;
    }
    }

    return OV7670_EOK;
}

/**
 * @brief       设置OV7670模块对比度
 * @param       contrast:   OV7670_CONTRAST_0: -2
 *                          OV7670_CONTRAST_1: -1
 *                          OV7670_CONTRAST_2: 0
 *                          OV7670_CONTRAST_3: +1
 *                          OV7670_CONTRAST_4: +2
 * @retval      OV7670_EOK   : 设置OV7670模块对比度成功
 *              OV7670_EINVAL: 传入参数错误
 */
uint8_t ov7670_set_contrast(ov7670_contrast_t contrast) {
    switch (contrast) {
    case OV7670_CONTRAST_0: {
        ov7670_write_reg(0x56, 0X30);
        break;
    }
    case OV7670_CONTRAST_1: {
        ov7670_write_reg(0x56, 0X38);
        break;
    }
    case OV7670_CONTRAST_2: {
        ov7670_write_reg(0x56, 0X40);
        break;
    }
    case OV7670_CONTRAST_3: {
        ov7670_write_reg(0x56, 0X50);
        break;
    }
    case OV7670_CONTRAST_4: {
        ov7670_write_reg(0x56, 0X60);
        break;
    }
    case OV7670_CONTRAST_8: {
        ov7670_write_reg(0x56, 0X70);
        break;
    }

    default: {
        return OV7670_EINVAL;
    }
    }

    return OV7670_EOK;
}

/**
 * @brief       设置OV7670模块特殊效果
 * @param       contrast: OV7670_SPECIAL_EFFECT_NORMAL  : Normal
 *                        OV7670_SPECIAL_EFFECT_BW      : B&W
 *                        OV7670_SPECIAL_EFFECT_BLUISH  : Bluish
 *                        OV7670_SPECIAL_EFFECT_SEPIA   : Sepia
 *                        OV7670_SPECIAL_EFFECT_REDISH  : Redish
 *                        OV7670_SPECIAL_EFFECT_GREENISH: Greenish
 *                        OV7670_SPECIAL_EFFECT_NEGATIE : Negative
 * @retval      OV7670_EOK   : 设置OV7670模块特殊效果成功
 *              OV7670_EINVAL: 传入参数错误
 */
uint8_t ov7670_set_special_effect(ov7670_special_effect_t effect) {
    switch (effect) {
    case OV7670_SPECIAL_EFFECT_NORMAL: {
        ov7670_write_reg(0x3A, 0x0D);
        ov7670_write_reg(0x68, 0x80);
        ov7670_write_reg(0X67, 0x80);
        break;
    }
    case OV7670_SPECIAL_EFFECT_BW: {
        ov7670_write_reg(0x3A, 0x14);
        ov7670_write_reg(0x68, 0x80);
        ov7670_write_reg(0X67, 0x80);
        break;
    }
    case OV7670_SPECIAL_EFFECT_BLUISH: {
        ov7670_write_reg(0x3A, 0x14);
        ov7670_write_reg(0x68, 0x80);
        ov7670_write_reg(0X67, 0xC0);
        break;
    }
    case OV7670_SPECIAL_EFFECT_SEPIA: {
        ov7670_write_reg(0x3A, 0x14);
        ov7670_write_reg(0x68, 0xA0);
        ov7670_write_reg(0X67, 0x40);
        break;
    }
    case OV7670_SPECIAL_EFFECT_REDISH: {
        ov7670_write_reg(0x3A, 0x14);
        ov7670_write_reg(0x68, 0xC0);
        ov7670_write_reg(0X67, 0x80);
        break;
    }
    case OV7670_SPECIAL_EFFECT_GREENISH: {
        ov7670_write_reg(0x3A, 0x14);
        ov7670_write_reg(0x68, 0x40);
        ov7670_write_reg(0X67, 0x40);
        break;
    }
    case OV7670_SPECIAL_EFFECT_NEGATIVE: {
        ov7670_write_reg(0x3A, 0x24);
        ov7670_write_reg(0x68, 0x80);
        ov7670_write_reg(0X67, 0x80);
        break;
    }
    default: {
        return OV7670_EINVAL;
    }
    }

    return OV7670_EOK;
}

/**
 * @brief       设置OV7670模块输出模式
 * @param       width : 输出图像宽度（VGA，<=640；QVGA，<=320）
 *              height: 输出图像高度（VGA，<=480；QVGA，<=240）
 *              mode  : OV7670_OUTPUT_MODE_VGA : VGA输出模式
 *                      OV7670_OUTPUT_MODE_QVGA: QVGA输出模式
 * @retval      OV7670_EOK   : 设置OV7670模块输出模式成功
 *              OV7670_EINVAL: 传入参数错误
 *              OV7670_ENOMEM: 内存不足
 */
// uint8_t ov7670_set_output(uint16_t width, uint16_t height, ov7670_output_mode_t mode)
// {
//     uint16_t xs;
//     uint16_t ys;
//     uint8_t hstart_raw;
//     uint8_t hstart_new;
//     uint8_t vstrt_raw;
//     uint8_t vstrt_new;
//     uint8_t href_raw;
//     uint8_t href_new;
//     uint8_t exhch;

// #if (OV7670_USING_DCMI == 0)
//     myfree(SRAMIN, g_ov7670_sta.read.line_buf);
//     g_ov7670_sta.read.line_buf = mymalloc(SRAMIN, width * sizeof(uint16_t));
//     if (g_ov7670_sta.read.line_buf == NULL)
//     {
//         g_ov7670_sta.read.line_buf = mymalloc(SRAMIN, g_ov7670_sta.output.width * sizeof(uint16_t));
//         return OV7670_ENOMEM;
//     }
// #endif

//     switch (mode)
//     {
//         case OV7670_OUTPUT_MODE_VGA:
//         {
//             if ((width > OV7670_VGA_WIDTH_MAX) || (height > OV7670_VGA_HEIGHT_MAX))
//             {
//                 return OV7670_EINVAL;
//             }
//             break;
//         }
//         case OV7670_OUTPUT_MODE_QVGA:
//         {
//             if ((width > OV7670_QVGA_WIDTH_MAX) || (height > OV7670_QVGA_HEIGHT_MAX))
//             {
//                 return OV7670_EINVAL;
//             }
//             break;
//         }
//         default:
//         {
//             return OV7670_EINVAL;
//         }
//     }

//     switch (mode)
//     {
//         case OV7670_OUTPUT_MODE_VGA:
//         {
//             xs = (OV7670_VGA_WIDTH_MAX - width) >> 1;
//             ys = (OV7670_VGA_HEIGHT_MAX - height) >> 1;
//             ov7670_write_reg(OV7670_REG_COM7, 0x06);
//             ov7670_write_reg(OV7670_REG_HSTART, 0x23);
//             ov7670_write_reg(OV7670_REG_HSIZE, 0xA0);
//             ov7670_write_reg(OV7670_REG_VSTRT, 0x07);
//             ov7670_write_reg(OV7670_REG_VSIZE, 0xF0);
//             ov7670_write_reg(OV7670_REG_HREF, 0x00);
//             ov7670_write_reg(OV7670_REG_HOutSize, 0xA0);
//             ov7670_write_reg(OV7670_REG_VOutSize, 0xF0);
//             break;
//         }
//         case OV7670_OUTPUT_MODE_QVGA:
//         {
//             xs = (OV7670_QVGA_WIDTH_MAX - width) >> 1;
//             ys = (OV7670_QVGA_HEIGHT_MAX - height) >> 1;
//             ov7670_write_reg(OV7670_REG_COM7, 0x46);
//             ov7670_write_reg(OV7670_REG_HSTART, 0x3F);
//             ov7670_write_reg(OV7670_REG_HSIZE, 0x50);
//             ov7670_write_reg(OV7670_REG_VSTRT, 0x03);
//             ov7670_write_reg(OV7670_REG_VSIZE, 0x78);
//             ov7670_write_reg(OV7670_REG_HREF, 0x00);
//             ov7670_write_reg(OV7670_REG_HOutSize, 0x50);
//             ov7670_write_reg(OV7670_REG_VOutSize, 0x78);
//             break;
//         }
//         default:
//         {
//             return OV7670_EINVAL;
//         }
//     }

//     hstart_raw = ov7670_read_reg(OV7670_REG_HSTART);
//     hstart_new = hstart_raw + (xs >> 2);
//     ov7670_write_reg(OV7670_REG_HSTART, hstart_new);
//     ov7670_write_reg(OV7670_REG_HSIZE, width >> 2);
//     g_ov7670_sta.output.width = ov7670_read_reg(OV7670_REG_HSIZE) << 2;

//     vstrt_raw = ov7670_read_reg(OV7670_REG_VSTRT);
//     vstrt_new = vstrt_raw + (ys >> 1);
//     ov7670_write_reg(OV7670_REG_VSTRT, vstrt_new);
//     ov7670_write_reg(OV7670_REG_VSIZE, height >> 1);
//     g_ov7670_sta.output.height = ov7670_read_reg(OV7670_REG_VSIZE) << 1;

//     href_raw = ov7670_read_reg(OV7670_REG_HREF);
//     href_new = ((ys & 0x01) << 6) | ((xs & 0x03) << 4) | ((height & 0x01) << 2) | (width & 0x03) | href_raw;
//     ov7670_write_reg(OV7670_REG_HREF, href_new);

//     ov7670_write_reg(OV7670_REG_HOutSize, width >> 2);
//     ov7670_write_reg(OV7670_REG_VOutSize, height >> 1);

//     exhch = (href_raw | (width & 0x03) | ((height & 0x01) << 2));
//     ov7670_write_reg(OV7670_REG_EXHCH, exhch);

//     return OV7670_EOK;
// }

// /**
//  * @brief       设置OV7670模块时钟预分频系数
//  * @param       clk_rc: Internal clock pre-scalar（0~63）
//  * @retval      OV7670_EOK   : 设置OV7670模块时钟预分频系数成功
//  *              OV7670_EINVAL: 传入参数错误
//  */
// uint8_t ov7670_set_output_speed(uint8_t clk_rc)
// {
//     if (clk_rc > 63)
//     {
//         return OV7670_EINVAL;
//     }

//     ov7670_write_reg(OV7670_REG_CLKRC, clk_rc);

//     return OV7670_EOK;
// }

// /**
//  * @brief       开启OV7670模块彩条测试
//  * @param       无
//  * @retval      无
//  */
// void ov7670_colorbar_enable(void)
// {
//     uint8_t com3;

//     com3 = ov7670_read_reg(OV7670_REG_COM3);
//     com3 |= (uint8_t)(1 << 0);
//     ov7670_write_reg(OV7670_REG_COM3, com3);
// }

// /**
//  * @brief       关闭OV7670模块彩条测试
//  * @param       无
//  * @retval      无
//  */
// void ov7670_colorbar_disable(void)
// {
//     uint8_t com3;

//     com3 = ov7670_read_reg(OV7670_REG_COM3);
//     com3 &= ~(uint8_t)(1 << 0);
//     ov7670_write_reg(OV7670_REG_COM3, com3);
// }

/**
 * @brief       获取OV7670模块输出图像宽度
 * @param       无
 * @retval      OV7670模块输出图像宽度
 */
uint16_t ov7670_get_output_width(void) {
    return g_ov7670_sta.output.width;
}

/**
 * @brief       获取OV7670模块输出图像高度
 * @param       无
 * @retval      OV7670模块输出图像高度
 */
uint16_t ov7670_get_output_height(void) {
    return g_ov7670_sta.output.height;
}

/**
 * @brief       获取OV7670模块输出的一帧图像数据
 * @param       dts_addr : 帧数据的接收缓冲的首地址
 *              before_transfer: 帧数据传输前，需要完成的事务，可为NULL
 * @retval      OV7670_EOK   : 获取OV7670模块输出的一帧图像数据成功
 *              OV7670_EINVAL: 传入参数错误
 *              OV7670_EEMPTY: 图像数据为空
 */
uint8_t OV7670GetFrame(uint32_t dts_addr, ov7670_get_type_t type, void (*before_transfer)(void)) {
    uint32_t meminc;
    uint32_t memdataalignment;
    uint32_t len;
    uint8_t pixelLen;

    switch (type.bufferLength) {
    case OV7670_GET_TYPE_BUFFER_8B: {
        memdataalignment = DMA_MDATAALIGN_BYTE;
        pixelLen = sizeof(uint8_t);
        break;
    }
    case OV7670_GET_TYPE_BUFFER_16B: {
        memdataalignment = DMA_MDATAALIGN_HALFWORD;
        pixelLen = sizeof(uint16_t);
        break;
    }
    case OV7670_GET_TYPE_BUFFER_32B: {
        memdataalignment = DMA_MDATAALIGN_WORD;
        pixelLen = sizeof(uint32_t);
        break;
    }
    default: {
        return OV7670_EINVAL;
    }
    }
    switch (type.bufferInc) {
    case OV7670_GET_TYPE_BUFFER_NOINC: {
        meminc = DMA_MINC_DISABLE;
        break;
    }
    case OV7670_GET_TYPE_BUFFER_INC: {
        meminc = DMA_MINC_ENABLE;
        break;
    }
    default: {
        return OV7670_EINVAL;
    }
    }
    switch (type.bufferType) {
    case OV7670_GET_TYPE_BUFFER_LINE: {
        len = g_ov7670_sta.output.width / pixelLen;
        break;
    }
    case OV7670_GET_TYPE_BUFFER_FRAME: {
        len = (g_ov7670_sta.output.width * g_ov7670_sta.output.height) / pixelLen;
        break;
    }
    default: {
        return OV7670_EINVAL;
    }
    }
    if (before_transfer != NULL) {
        before_transfer();
    }
    ov7670_dcmi_start(dts_addr, meminc, memdataalignment, len);

    return OV7670_EOK;
}
